﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RonaldCamcam.XBeeFramework.Api;

namespace RonaldCamcam.XBeeApiFramework.Utilility
{
    public class FrameFactory
    {
        internal static byte[] BuildCommand(byte[] commandParameter, ApiId apiIdentifier, Encoding enc)
        {            
            int checkSum = 0;
            //ApiDelimiter + commandParameterWithAck
            int totalPayload = 1 + commandParameter.Length;

            Queue<byte> frame = new Queue<byte>();
            frame.Enqueue(ApiBase.ApiDelimiter);

            #region Step 2. Set Length
            //initialize length
            byte[] msbLsb = new byte[2] { 0x00, 0x00 };
            //get bytes for the total payload
            byte[] payloadByte = enc.GetBytes(new char[] { (char)totalPayload });
            //check if length has two bytes
            if (payloadByte.Length > 1)
            {
                msbLsb = payloadByte;
            }
            else
            {
                //otherwise update the LSB
                msbLsb[1] = payloadByte[payloadByte.Length - 1];
            }
            //add to queue
            foreach (byte b in msbLsb)
            {
                frame.Enqueue(b);
            }
            #endregion

            #region Step 3. Frame Data
            //Sub step a: Set Identifier
            frame.Enqueue((byte)apiIdentifier);
            checkSum += (byte)apiIdentifier;
            //Sub step b: Set ACK, two character AT command text and n-parameters
            foreach (byte b in commandParameter)
            {
                frame.Enqueue(b);
                checkSum += b;
            }
            #endregion
            #region Step 4. Set Checksum
            int chkSum = (255 - checkSum) & 255;            
            
            foreach (byte b in enc.GetBytes(new char[] { (char)chkSum }))
            {
                frame.Enqueue(b);
            }
            #endregion

            return frame.ToArray();
        }

        internal static byte[] BuildRemoteCommand(byte[] destinationNetworkAddress, byte[] parameters, ApiId apiIdentifier, ApplyRemoteChanges applyChanges, Encoding enc)
        {
            byte frameId = parameters[0];
            byte[] atCommand = new byte[2] { parameters[1], parameters[2]};
            List<byte> atCommandValue = new List<byte>();
            for (int x = 3; x < parameters.Length; x++)
            {
                atCommandValue.Add(parameters[x]);
            }

            //set default network address to 0xFFFE
            byte[] networkAddress = new byte[] { 0xFF, 0xFE };
            byte[] destinationAddress = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

            if (destinationNetworkAddress.Length == 8)
            {
                //need to make sure we have 64bit/8Bytes address, all data after that is discarded
                for (int ctr = 0; ctr < destinationNetworkAddress.Length; ctr++)
                {
                    if (ctr < destinationAddress.Length)
                        destinationAddress[ctr] = destinationNetworkAddress[ctr];
                }
            }
            else if (destinationNetworkAddress.Length == 2)
            {
                //this is a network address
                for (int ctr = 0; ctr < destinationNetworkAddress.Length; ctr++)
                {
                    if (ctr < networkAddress.Length)
                        networkAddress[ctr] = destinationNetworkAddress[ctr];
                }
            }
            else
            {
                throw new ApplicationException("Destination address is not valid");
            }

            int checkSum = 0;            
            int totalPayload = 1                        //1 byte api id + 
                + 1                                     //1 byte frame id + 
                + destinationAddress.Length             //8 byte destination address + 
                + networkAddress.Length                 //2 byte network address + 
                + 1                                     //1 byte apply changes +
                + atCommand.Length                      //2 byte AT Command
                + atCommandValue.Count;                 //n bytes data

            Queue<byte> frame = new Queue<byte>();
            frame.Enqueue(ApiBase.ApiDelimiter);

            #region Step 2. Set Length
            //initialize length
            byte[] msbLsb = new byte[2] { 0x00, 0x00 };
            //get bytes for the total payload
            byte[] payloadByte = enc.GetBytes(new char[] { (char)totalPayload });
            //check if length has two bytes
            if (payloadByte.Length > 1)
            {
                msbLsb = payloadByte;
            }
            else
            {
                //otherwise update the LSB
                msbLsb[1] = payloadByte[payloadByte.Length - 1];
            }
            //add to queue
            foreach (byte b in msbLsb)
            {
                frame.Enqueue(b);
            }
            #endregion
            #region Step 3. Frame Data
            #region Set Api Identifier
            frame.Enqueue((byte)apiIdentifier);
            checkSum += (byte)apiIdentifier;
            #endregion
            #region Set ACK if response is needed
            frame.Enqueue(frameId);
            checkSum += frameId;
            #endregion
            #region Set Destination Address
            foreach (byte b in destinationAddress)
            {
                frame.Enqueue(b);
                checkSum += b;
            }
            #endregion
            #region Set Network Address
            foreach (byte b in networkAddress)
            {
                frame.Enqueue(b);
                checkSum += b;
            }
            #endregion
            #region Apply Changes
            frame.Enqueue((byte)applyChanges);
            checkSum += (byte)applyChanges;
            #endregion
            #region Set two character AT command text
            foreach (byte b in atCommand)
            {
                frame.Enqueue(b);
                checkSum += b;
            }
            #endregion
            #region Set parameters
            if (atCommandValue.Count != 0)
            {
                foreach (byte b in atCommandValue)
                {
                    frame.Enqueue(b);
                    checkSum += b;
                }
            }
            #endregion
            #endregion
            #region Step 4. Set Checksum
            byte[] chkSum = BitConverter.GetBytes((255 - checkSum) & 255);
            frame.Enqueue(chkSum[0]);           
            #endregion

            return frame.ToArray();
        }
    }
}
